﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.Win32;

namespace DAXT
{
    public partial class FormUpdateSSDelta : Form
    {
        string[] settingsLines;

        const string folderDAXTSSDelta = @"C:\DAXTSSDelta";

        List<string> listChangedObjects;

        Boolean isInSyncMode;
        string pathFolderDAXTSSDeltaDate;
        string pathFolderDAXTSSDeltaDateXPOs;        
        string pathFolderToBeImported;
        string pathFileDAXTSSDeltaLog;
        string pathFileListChangeSets;
        string pathFileListChangeSetsSummary;
        string pathFileListChangedObjects;
        string pathFileCombinedXPOs;
        string pathFileCombinedXPOsLog;

        TfsTeamProjectCollection tfsProjects;
        VersionControlServer vcService;

        List<string> changedFiles = new List<string>();

        #region Form methods

        public FormUpdateSSDelta()
        {
            InitializeComponent();
        }

        private void UpdateSSDelta_Load(object sender, EventArgs e)
        {
            textBoxFromDate.Text = DateTime.Today.ToString("MM/dd/yyyy");

            if (!Directory.Exists(folderDAXTSSDelta))
            {
                Directory.CreateDirectory(folderDAXTSSDelta);
            }

            /*
            if (!Directory.Exists(folderNewXPO))
            {
                Directory.CreateDirectory(folderNewXPO);
            }
             */

            if (File.Exists("Settings.txt"))
            {
                settingsLines = File.ReadAllLines("Settings.txt");

                switch (settingsLines.Length)
                {
                    case (2):
                        textBoxServer.Text = settingsLines[0];
                        textBoxBranchPath.Text = settingsLines[1];
                        break;

                    case (3):
                        textBoxServer.Text = settingsLines[0];
                        textBoxBranchPath.Text = settingsLines[1];
                        textBoxCombinePath.Text = settingsLines[2];
                        break;
                }
            }
        }

        #endregion

        #region Create project from C#

        /// <summary>
        /// Might use this later to generate the ax project xpo from here; the only issue is that I should know all the mappings of the objects which is rather dificult;
        /// I'll better go with a change in the sys classes to create the project when importing the files that will outputLines the xpo file
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private void CreateProject()
        {
            TextWriter oFile = new StreamWriter("noFile");

            string sT = "Exportfile for AOT version 1.0 or later"; oFile.WriteLine(sT);
            sT = "Formatversion: 1"; oFile.WriteLine(sT);
            sT = "";  oFile.WriteLine(sT);
            sT = "***Element: PRN"; oFile.WriteLine(sT);
            sT = ""; oFile.WriteLine(sT);
            sT = "; Microsoft Dynamics AX Project : SSDelta unloaded"; oFile.WriteLine(sT);
            sT = "; --------------------------------------------------------------------------------"; oFile.WriteLine(sT);
            sT = "  PROJECTVERSION 2"; oFile.WriteLine(sT);
            sT = ""; oFile.WriteLine(sT);
            sT = "PROJECT #EVW_Generate"; oFile.WriteLine(sT);
            sT = "   SHARED"; oFile.WriteLine(sT);
            sT = "  PROPERTIES"; oFile.WriteLine(sT);
            sT = "    Name                #Delta"; oFile.WriteLine(sT);
            sT = "  ENDPROPERTIES"; oFile.WriteLine(sT);
            sT = ""; oFile.WriteLine(sT);
            sT = "    PROJECTCLASS ProjectNode"; oFile.WriteLine(sT);
            
            foreach (string s in this.changedFiles)
            {
                string s1;
                s1 = s.Substring(0, s.Length - 4);
                if (s1.ToLower().IndexOf("resources") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "21", "820", "0");
                }
                if (s1.ToLower().IndexOf("outputLines") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "3", "296", "0");
                }
                if (s1.ToLower().IndexOf("action") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "2", "296", "0");
                }
                if (s1.ToLower().IndexOf("display") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "1", "296", "0");
                }
                if (s1.ToLower().IndexOf("menus") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "16", "205", "0");
                }
                if (s1.ToLower().IndexOf("jobs") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "5", "215", "0");
                }
                if (s1.ToLower().IndexOf("queries") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "20", "330", "0");
                }
                if (s1.ToLower().IndexOf("reports") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "18", "202", "0");
                }
                if (s1.ToLower().IndexOf("forms") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "11", "201", "0");
                }
                if (s1.ToLower().IndexOf("classes") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "45", "329", "0");
                }
                if (s1.ToLower().IndexOf("macros") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "4", "218", "0");
                }
                if (s1.ToLower().IndexOf("security keys") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "36", "313", "0");
                }
                if (s1.ToLower().IndexOf("configuration keys") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "35", "312", "0");
                }
                if (s1.ToLower().IndexOf("licence codes") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "15", "311", "0");
                }
                if (s1.ToLower().IndexOf("base enums") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "40", "209", "0");
                }
                if (s1.ToLower().IndexOf("extended data types") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "41", "228", "0");
                }
                if (s1.ToLower().IndexOf("views") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "44", "243", "0");
                }
                if (s1.ToLower().IndexOf("maps") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "44", "236", "0");
                }
                if (s1.ToLower().IndexOf("tables") != -1)
                {
                    this.WriteToPrj(oFile, s1.Substring(s.IndexOf("/") + 1), "44", "204", "0");
                }
            }

            sT = "  ENDPROJECT"; oFile.WriteLine(sT);
            sT = ""; oFile.WriteLine(sT);
            sT = ""; oFile.WriteLine(sT);
            sT = "***Element: END"; oFile.WriteLine(sT);


            oFile.Close();

            MessageBox.Show("Done", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void WriteToPrj(TextWriter oFile, string sName, string utiltype, string nodetype, string objectid)
        {
            string sT;

            sT = "    BEGINNODE"; oFile.WriteLine(sT);
            sT = "      FILETYPE 0"; oFile.WriteLine(sT);
            sT = "      UTILTYPE " + utiltype; oFile.WriteLine(sT);
            sT = "      UTILOBJECTID " + objectid; oFile.WriteLine(sT);
            sT = "      NODETYPE " + nodetype; oFile.WriteLine(sT);
            sT = "      NAME #" + sName; oFile.WriteLine(sT);
            sT = "    ENDNODE"; oFile.WriteLine(sT);
        }

        #endregion

        #region Event handlers

        private void buttonSelectFile_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                textBoxCombinePath.Text = openFileDialog1.FileName;
            }
        }

        private void buttonGetDelta_Click(object sender, EventArgs e)
        {
            try
            {
                string lastSyncDate = lastSyncDate = textBoxFromDate.Text;

                pathFolderDAXTSSDeltaDate = Path.Combine(folderDAXTSSDelta, DateTime.Today.ToString("yyyy-MM-dd"));
                pathFolderDAXTSSDeltaDate = Path.Combine(pathFolderDAXTSSDeltaDate, DateTime.Now.ToString("HH-mm-ss"));

                if (!Directory.Exists(pathFolderDAXTSSDeltaDate))
                {
                    Directory.CreateDirectory(pathFolderDAXTSSDeltaDate);
                }
                else
                {
                    throw new System.Exception("You can run the application once per minute. Please wait one more minute.");
                }

                pathFolderDAXTSSDeltaDateXPOs = Path.Combine(pathFolderDAXTSSDeltaDate, "NewXPOs");

                Directory.CreateDirectory(pathFolderDAXTSSDeltaDateXPOs);

                pathFolderToBeImported = Path.Combine(pathFolderDAXTSSDeltaDate, "ToBeImported");

                Directory.CreateDirectory(pathFolderToBeImported);

                isInSyncMode = true;
                pathFileDAXTSSDeltaLog = Path.Combine(pathFolderDAXTSSDeltaDate, "Output.txt");
                pathFileListChangeSets = Path.Combine(pathFolderDAXTSSDeltaDate, "ListChangeSets.txt");
                pathFileListChangeSetsSummary = Path.Combine(pathFolderDAXTSSDeltaDate, "ListChangeSetsSummary.txt");
                pathFileListChangedObjects = Path.Combine(pathFolderDAXTSSDeltaDate, "ListChangedObjects.txt");
                pathFileCombinedXPOs = Path.Combine(pathFolderToBeImported, "CombinedXPOs.xpo");
                pathFileCombinedXPOsLog = Path.Combine(pathFolderDAXTSSDeltaDate, "CombinedXPOs.log");

                ConnectTFS();
                GetFiles(lastSyncDate);
                File.Copy(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), @"External\Class_IEIDEImportVSProject.xpo"), Path.Combine(pathFolderDAXTSSDeltaDateXPOs, @"Class_IEIDEImportVSProject.xpo"));
                SaveSettings();
                CombineXPOs();

                OutputLog("* Got delta code.\r\n");
                OutputLog("");
                MessageBox.Show("Got delta code.", "Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);

                Process.Start(pathFolderDAXTSSDeltaDate, "open");
            }
            catch (Exception ex)
            {
                OutputLog(ex.Message);
                OutputLog("");
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        #endregion

        #region Do the work

        private void Import2AX()
        {
            try
            {
                ProcessStartInfo startInfo = new ProcessStartInfo();
                startInfo.CreateNoWindow = false;
                startInfo.UseShellExecute = true;
                startInfo.FileName = @"C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\ax32.exe";
                startInfo.WindowStyle = ProcessWindowStyle.Normal;

                startInfo.Arguments = string.Format(" -lazyclassloading -lazytableloading -internal=NoModalBoxes -model=MODEL -StartupCmd=aotimport_{0}", pathFileCombinedXPOs);

                try
                {
                    using (Process exeProcess = Process.Start(startInfo))
                    {
                        exeProcess.WaitForExit();
                    }
                }
                catch
                {
                }

                File.Copy(@"*.ald", @"C:\Temp\", true);
                File.Delete(@"C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAx\bin\Application\Appl\Standard\axen-us.ald");
                startInfo.Arguments = @" -lazyclassloading -lazytableloading -internal=NoModalBoxes -model=PCGModel -StartupCmd=aldimport_{0}";

                try
                {
                    using (Process exeProcess = Process.Start(startInfo))
                    {
                        exeProcess.WaitForExit();
                    }
                }
                catch
                {
                }

            }
            catch (Exception ex)
            {
                OutputLog(ex.Message);
            }

            OutputLog("Import of the objects done");
        }

        private void CombineXPOs()
        {
            if (File.Exists(textBoxCombinePath.Text))
            {
                Process combineProcess = new Process();

                ProcessStartInfo processInfo = new ProcessStartInfo();

                processInfo.FileName = textBoxCombinePath.Text;
                processInfo.Arguments = string.Format("-XPODir {0} -CombinedXPOFile {1} -Verbose", pathFolderDAXTSSDeltaDateXPOs, pathFileCombinedXPOs);
                processInfo.CreateNoWindow = false;
                processInfo.RedirectStandardOutput = true;
                processInfo.UseShellExecute = false;

                combineProcess.StartInfo = processInfo;

                combineProcess.Start();

                string outputLines = combineProcess.StandardOutput.ReadToEnd();

                combineProcess.WaitForExit();

                File.WriteAllText(pathFileCombinedXPOsLog, outputLines);

                OutputLog(string.Format("You have the combined xpo ready at {0}", pathFileCombinedXPOs));
            }
            else
            {
                throw new System.Exception("You do not have the CombineXPOS.exe application. Please run the tool on a system that has this application.");
            }
        }

        private void SaveSettings()
        {
            settingsLines = new string[3];

            settingsLines[0] = textBoxServer.Text;
            settingsLines[1] = textBoxBranchPath.Text;
            settingsLines[2] = textBoxCombinePath.Text;

            File.WriteAllLines("Settings.txt", settingsLines);
        }

        private void ConnectTFS()
        {
            tfsProjects = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(textBoxServer.Text));
            vcService = (VersionControlServer)tfsProjects.GetService(typeof(VersionControlServer));

            OutputLog("* Connected " + DateTime.Now.ToShortTimeString() + ".");
        }

        private void GetFiles(string lastSyncDate)
        {
            string  completeSavePath;

            listChangedObjects = new List<string>();

            try
            {
                foreach (Changeset item in vcService.QueryHistory(textBoxBranchPath.Text, VersionSpec.ParseSingleSpec("D" + textBoxFromDate.Text, null), 0,
                        RecursionType.Full, null, VersionSpec.ParseSingleSpec("D" + textBoxFromDate.Text, null),
                        null, Int32.MaxValue, true, false))
                {
                    DateTime checkInDate = item.CreationDate;
                    string user = item.Committer;
                    string widesc = "";

                    if (item.WorkItems.Length > 0)
                    {
                        WorkItem wi = item.WorkItems[0];
                        if (wi != null)
                        {
                            widesc = "," + wi.Id + "," + wi.Title;
                        }
                    }

                    File.AppendAllText(pathFileListChangeSetsSummary, item.ChangesetId + "," + checkInDate + "," + user + "," + item.Comment.Substring(0, (item.Comment.Length > 70 ? 70 : item.Comment.Length)) + widesc + "\r\n");

                    // these are administrative checkins, ignore them
                    /*
                    if (user.ToLower() == "domain\\adminuser")
                    {
                        continue;
                    }
                     * */

                    if (checkInDate <= DateTime.Parse(textBoxFromDate.Text))
                    {
                        continue;
                    }

                    foreach (Change Itemchange in item.Changes)
                    {
                        File.AppendAllText(pathFileListChangeSets, "\t" + Itemchange.Item.ServerItem.ToString() + " || " + Itemchange.ChangeType.ToString() + " || " + Itemchange.Item.ItemType.ToString() + " || " + Itemchange.Item.CheckinDate.ToString() + "\r\n");

                        if (Itemchange.ChangeType != ChangeType.Delete &&
                            Itemchange.ChangeType != ChangeType.Branch &&
                            Itemchange.ChangeType != (ChangeType.Delete | ChangeType.SourceRename))
                        {
                            if (Itemchange.Item.ItemType == ItemType.File)
                            {
                                if (!vcService.ServerItemExists(Itemchange.Item.ServerItem, ItemType.File))
                                {
                                    // the queryHistory will return us the items in chronological order
                                    // so we might retrieve some items that are deleted or renamed, thus not exist anymore
                                    // we want to check this on the latest version of the object and do not include them

                                    // too slow: vcService.DownloadFile(Itemchange.Item.ServerItem, Path.Combine(folderNewXPO, "File.tmp"));
                                    File.AppendAllText(pathFileListChangeSets, "\t\t" + "Item will NOT be added (latest removed)" + "\r\n");

                                    continue;
                                }

                                File.AppendAllText(pathFileListChangeSets, "\t\t" + "Item will be added" + "\r\n");

                                string pathFileName = Itemchange.Item.ServerItem.Replace(@"$/", "");

                                if (!this.listChangedObjects.Contains(pathFileName))
                                {
                                    this.listChangedObjects.Add(pathFileName);
                                    completeSavePath = Path.Combine(pathFolderDAXTSSDeltaDateXPOs, pathFileName.Replace(@"/", @"\"));
                                    Itemchange.Item.DownloadFile(completeSavePath);

                                    if (completeSavePath.IndexOf(".ald") > 0)
                                    {
                                        string fileName = Path.GetFileName(completeSavePath); // completeSavePath.Substring(completeSavePath.LastIndexOf(@"\") + 1);
                                        string pathFileCopy = Path.Combine(pathFolderToBeImported, fileName);

                                        File.Copy(completeSavePath, pathFileCopy);
                                    }

                                    if (completeSavePath.IndexOf("Visual Studio Projects") > 0)
                                    {
                                        string fileName = completeSavePath.Substring(completeSavePath.IndexOf("Visual Studio Projects"));
                                        string pathFileCopy = Path.Combine(pathFolderToBeImported, fileName);

                                        Directory.CreateDirectory(pathFileCopy.Substring(0, pathFileCopy.LastIndexOf(@"\")));

                                        File.Copy(completeSavePath, pathFileCopy);
                                    }
                                }
                            }
                        }
                        else
                        {
                            File.AppendAllText(pathFileListChangeSets, "\t\t" + "Item will NOT be added (remove operation)" + "\r\n");
                        }
                    }
                }

                listChangedObjects.Sort();

                foreach (string s in listChangedObjects)
                {
                    File.AppendAllText(pathFileListChangedObjects, s + "\r\n");
                }
            }
            catch (Exception ex)
            {
                throw new System.Exception("Error in getting latest.\r\n" + ex.Message + "\r\n");
            }

            OutputLog(string.Format("* Got {0} files from TFS.", listChangedObjects.Count));
        }

        private void OutputLog(string s)
        {
            if (!isInSyncMode)
            {
                MessageBox.Show(s, "Output", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            lstOutput.Items.Add(s);
            File.AppendAllText(pathFileDAXTSSDeltaLog, s + "\r\n");
        }

        #endregion
    }

    public static class Extensions
    {
        public static string ToSafeString(this object regValue)
        {
            return regValue != null ? regValue.ToString() : "";
        }
    }
}

